//=============================================================================

#ifndef ANET_H
  #define ANET_H

//=============================================================================

#include <Winsock2.h>
#include "doLog.h"
#include "aPacket.h"
#include "aPacketCloseConnection.h"

//=============================================================================

#define BUFFSIZE 4096

//=============================================================================

template <class fatherType> class aTransmission
{
  public:
    aTransmission()
    {
      workSock = 0;
      net = 0;
      threadId = 0;
      handle = 0;
    }
    aTransmission(SOCKET s, fatherType* n, const aPacket& p)
    {
      workSock = s;
      net = n;
      pack = p;
      threadId = 0;
      handle = 0;
    }
    ~aTransmission()
    {
    }
    
    SOCKET workSock;
    fatherType* net;
    aPacket pack;
    DWORD threadId;
    HANDLE handle;
};

//=============================================================================

enum antRet
{
  NET_DONE = -128,
  NET_WSASYSNOTREADY,
  NET_WSAVERNOTSUPPORTED,
  NET_WSAEINPROGRESS,
  NET_WSAEPROCLIM,
  NET_WSAEFAULT,
  NET_WSABADVERSION,
  NET_SOCKETBINDERROR,
  NET_NOSOCKET,
  NET_NOPACKETDATA,
  NET_CANTINTHREAD,
  NET_CANTOUTTHREAD,
  NET_UNKNOWNPACKET,
};

//=============================================================================

class aNet
{
  public:
    inline unsigned long anGetIn()  {return totalIn;}
    inline unsigned long anGetOut() {return totalOut;}
    inline SOCKET anGetSocket()     {return sock;}
    inline antRet anSend(SOCKET s, const aPacket& packet)
    {
      aTransmission<aNet>* trans = new aTransmission<aNet>(s, this, packet);//clearing in "out"

      trans->handle = CreateThread(0, 0, out, (LPVOID)trans, 0, &trans->threadId);

      if (trans->handle == NULL)
        return NET_CANTINTHREAD;

      return NET_DONE;
    }
    inline antRet anReceive(SOCKET s)
    {
      aTransmission<aNet>* trans = new aTransmission<aNet>;//clearing in "~aNet"

      trans->workSock = s;
      trans->net = this;
      inTrans.alAddItem()->data = trans;

      trans->handle = CreateThread(0, 0, in, (LPVOID)trans, 0, &trans->threadId);

      if (trans->handle == NULL)
        return NET_CANTINTHREAD;

      return NET_DONE;
    }

    aNet();
    virtual ~aNet();

  protected:
    inline void anShutdownSock() {if (sock != 0){shutdown(sock, SD_SEND); closesocket(sock);}}
    inline void anSpawnSocket() {anShutdownSock(); sock = socket(AF_INET, SOCK_STREAM, 0);}

    antRet anInit();
    antRet anBindSock();
    void anDisconnect();
    void anShowLastError();
    void anDeInit();
    virtual antRet anAnalyzePacket(SOCKET s, const aPacket& p) = 0 {}
    static DWORD WINAPI out(LPVOID param)
    {
      if (!param)
        return 0;

      aTransmission<aNet>* trans = (aTransmission<aNet>*) param;

      if (!trans->workSock)
        return 0;

      if (trans->pack.apGetLength() <= 0)
        return 0;

      int r = send(trans->workSock, trans->pack.apGetData(), trans->pack.apGetLength(), NULL);

      trans->net->totalOut += r;

      delete trans;

      return 0;
    }
    static DWORD WINAPI in(LPVOID param)
    {
      if (!param)
        return 0;

      aTransmission<aNet>* trans = (aTransmission<aNet>*) param;

      if (!trans->workSock)
        return 0;

      char buff[BUFFSIZE] = {0};
      char res[BUFFSIZE] = {0};
      aPacket tmpPacket(0);
      int step = 0, e, r;

      while(1)
      {
        step = 0;
        memset(buff, 0, BUFFSIZE);
        r = recv(trans->workSock, buff, BUFFSIZE, NULL);

        if (r == (-1))
        {
          aPacket tmpPacket2(PAK_CLS_CON);
          trans->net->anAnalyzePacket(trans->workSock, tmpPacket2);

          break;
        }

        if (r <= 0)
          break;

        memcpy(res, buff, r);
        step += r;
        trans->net->totalIn += r;
        tmpPacket.apChewPacket(buff);

        e = -1;

        while(r != tmpPacket.apGetLength())
        {
          memset(buff, 0, BUFFSIZE);
          e = recv(trans->workSock, buff, BUFFSIZE, NULL);

          if (e > 0)
          {
            memcpy(&res[step], buff, e);
            r += e;
            step += e;
            trans->net->totalIn += e;
          }
          else
            break;
        }

        trans->net->anAnalyzePacket(trans->workSock, aPacket(res));

        if (e == 0)
          break;
      }

      return 0;
    }

  protected:
    WSADATA sData;
    bool sInit;
    SOCKET sock;
    u_short port;
    unsigned long totalIn;
    unsigned long totalOut;
    sockaddr_in addr;
    aList<aTransmission<aNet>*> inTrans;
};

//=============================================================================

#endif

//=============================================================================
